/**
 * 求范围内随机数的函数
 * @param {number} a 表示范围的整数
 * @param {number} b 表示范围的整数
 * @return {number} 返回范围内的随机整数
 */
function getRandom(a, b) {
    return Math.floor(Math.random() * Math.abs(a - b)) + Math.min(a, b)
}
/**
 * 获取随机颜色值的函数
 * @param {boolean} bool 是否需要16进制颜色值的布尔值
 * @return {string} color 随机颜色值
 */
function getColor(bool) {
    if(bool && typeof bool === 'boolean') { // 随机16进制颜色值
        var color = '#'
        for(var a = 0; a < 3; a++) {
            var num = getRandom(0, 256).toString(16)
            color += num.length === 1 ? '0' + num : num
        }
    } else {
        var arr = []
        for(var a = 0; a < 3; a++) {
            arr.push(getRandom(0, 256))
        }
        var color = 'rgb(' + arr.join() + ')'
    }
    return color
}
/**
 * 给标签批量设置样式的函数
 * @param {node} ele 要设置样式的标签
 * @param {object} obj 将所有要设置的样式键值对组成的对象
 */
function setStyle(ele, obj) {
    var cssText = ''
    var reg = /([a-z]+)([A-Z])/g
    for(var key in obj) {
        var cssValue = obj[key]
        key = key.replace(reg, '$1-$2').toLowerCase()
        cssText += key + ':' + cssValue + ';'
    }
    ele.style.cssText += cssText
}
/**
 * 阻止冒泡的函数
 * @param {object} e 事件对象
 */
function stopBubble(e) {
    e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true
}
/**
 * 阻止默认行为
 * @param {object} e 事件对象
 */
function stopDefault(e) {
    e.preventDefault ? e.preventDefault() : e.returnValue = false
}
/**
 * 动画函数
 * @param {node} ele 要设置动画的标签
 * @param {object} obj 要动画的属性和目标值组成的键值对
 * @param {function} fn 动画结束后要执行的函数
 */
function animate(ele, obj, fn = () => {}) {
    let num = 0
    for(let key in obj ) {
        num++
        let target = obj[key] 
        let l = getComputedStyle(ele)[key] 
        if(key === 'opacity') {
            l *= 100
            target *= 100
        }
        l = parseInt(l)
        let timer = setInterval(() => {
            let speed = (target - l) / 20 
            l += speed > 0 ? Math.ceil(speed) : Math.floor(speed) 
            ele.style[key] = key === 'opacity' ? l / 100 : l + 'px'
            if(l === target) {
                clearInterval(timer)
                --num === 0 ? fn() : ''
            }
        }, 17)
    }
}
/**
 * 防抖函数
 * @param {function} handler 事件触发后要执行的函数
 * @param {number} time 防抖的间隔事件
 * @returns 
 */
function noShake(handler, time) {
    var timer
    return function() {
        var _this = this
        var arg = arguments
        clearTimeout(timer)
        timer = setTimeout(function() {
            handler.call(_this, ...arg)
        }, time)
    }
}
/***************** 调用防抖函数示例 *****************/
/*
input.oninput = noShake(function() {
    console.log(this.value)
}, 200)

document.onmousemove = noShake(function(e) {
    console.log(e.pageX)
}, 300)
*/
/**
 * 节流的函数
 * @param {function} handler  事件触发后要执行的函数
 * @param {number} time 节流的间隔事件
 * @returns 
 */
function throttle(handler, time) {
    var startTime = +new Date()
    return function() {
        var endTime = +new Date()
        if(endTime - startTime >= time) {
            handler.call(this, ...arguments)
            startTime = endTime
        }
    }
}

////////////////////// 操作cookie的函数 //////////////////////
/**
 * 设置cookie的函数
 * @param {string} key cookie的键
 * @param {string} value cookie的值
 * @param {number} seconds 设置cookie的存活秒数
 * @param {string} path 设置cookie的路径
 */
function setCookie(key, value, seconds, path = '/') {
    let date = new Date()
    date.setTime(date.getTime() - 8 * 3600 * 1000 + seconds * 1000)
    document.cookie = `${key}=${value};expires=${date};path=${path}`
}
/**
 * 获取cookie的函数
 * @param {string} key cookie的键
 * @returns {string} cookie键对应的值
 */
function getCookie(key) {
    let cookies = document.cookie
    if(!cookies) {
        return
    }
    let cookiesArr = cookies.split('; ')
    for(let a = 0; a < cookiesArr.length; a++) {
        let brr = cookiesArr[a].split('=')
        if(brr[0] === key) {
            return brr[1]
        }
    }
}
/**
 * 删除cookie的函数
 * @param {string} key cookie的键
 * @param {string} path cookie的路径
 */
function removeCookie(key, path = '/') {
    setCookie(key, null, -1, path)
}
/**
 * 发送ajax请求的函数
 * @param {object} obj 发送请求需要的参数
 * obj = {
 *  method: '请求方式，默认为get',
 *  url: '请求路径，必填',
 *  async: 是否异步的布尔值，默认为true,
 *  dataType: '指定响应回来的数据是什么格式的，json/text/xml，默认json',
 *  success(res) {} // 请求成功后要执行的函数
 *  error(err) {} // 请求失败后要执行的函数
 * }
 */
function request(obj) {
    // 限制各个数据
    // 请求路径
    if(obj.url === undefined) {
        throw new Error('请求路径不能为空！')
    }
    // 判断类型
    if(typeof obj.url != 'string') {
        throw new Error('请求路径错误！')
    }
    // dataType
    if(obj.dataType === undefined) {
        obj.dataType = 'json'
    }
    // 请求方式 - get/put/delete/post都可以，大小写都可以，可以不传，默认get
    if(obj.method === undefined) { // 如果没有传递
        // 给默认值get
        obj.method = 'get'
    }
    // 说明get传递了 - 限制是否是这4种方式
    if(obj.method.toLowerCase() != 'get' && obj.method.toLowerCase() != 'post' && obj.method.toLowerCase() != 'put' && obj.method.toLowerCase() != 'delete') {
        // 抛出自定义错误
        throw new Error('请求方式不正确！')
    }
    // async
    if(obj.async === undefined) {
        obj.async = true
    }
    if(typeof obj.async != 'boolean') {
        throw new Error('同步false异步true！')
    }
    // success
    if(obj.success === undefined) {
        obj.success = () => {}
    }
    if(typeof obj.success != 'function') {
        throw new Error('success是函数！')
    }
    // error
    if(obj.error === undefined) {
        obj.error = () => {}
    }
    if(typeof obj.error != 'function') {
        throw new Error('error是函数！')
    }
    // data - 可以是查询字符串，方便传递的话个，给对象更好
    // ajax语法中要求必须是查询字符串 - 如果是对象，需要转成查询字符串
    let data
    if(obj.data != undefined) {
        if(Object.prototype.toString.call(obj.data) === '[object Object]') {
            // 对象 - 转字符串
            let arr = []
            for(let key in obj.data) {
                arr.push(key + '=' + obj.data[key])
            }
            data = arr.join('&')
        } else if(Object.prototype.toString.call(obj.data) === '[object String]') {
            // 字符串
            data = obj.data
        } else {
            throw new Error('data是字符串或对象！')
        }
        // 判断请求方式是否是get
        if(obj.method.toLowerCase() === 'get') {
            obj.url += '?' + data
        }
    }
    var xhr = new XMLHttpRequest;
    xhr.open(obj.method, obj.url, obj.async);
    xhr.onreadystatechange = function(){
        if(xhr.readyState === 4){
            if(xhr.status >= 200 && xhr.status < 300){
                let res
                switch(obj.dataType) {
                    case 'xml':
                        res = xhr.responseXML
                    break
                    case 'json':
                        res = xhr.responseText
                        res = JSON.parse(res)
                    break
                    case 'text':
                        res = xhr.responseText
                    break
                    default:
                        throw new Error('dataType必须是json/text/xml！')
                }
                obj.success(res)
            } else {
                obj.error('错误')
            }
        }
    }
    if(obj.method.toLowerCase() === 'post' && data) {
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
        xhr.send(data)
        return
    }
    xhr.send()
}

function promiseRequest(obj) {
    return new Promise(function(resolve, reject) {
        if(obj.url === undefined) throw new Error('请求路径不能为空！')
        if(typeof obj.url != 'string') throw new Error('请求路径错误！')
        if(obj.dataType === undefined) obj.dataType = 'json'
        if(obj.method === undefined) obj.method = 'get'
        if(obj.method.toLowerCase() != 'get' && obj.method.toLowerCase() != 'post' && obj.method.toLowerCase() != 'put' && obj.method.toLowerCase() != 'delete') throw new Error('请求方式不正确！')
        if(obj.async === undefined) obj.async = true
        if(typeof obj.async != 'boolean') throw new Error('同步false异步true！')
        let data
        if(obj.data != undefined) {
            if(Object.prototype.toString.call(obj.data) === '[object Object]') {
                let arr = []
                for(let key in obj.data) {
                    arr.push(key + '=' + obj.data[key])
                }
                data = arr.join('&')
            } else if(Object.prototype.toString.call(obj.data) === '[object String]') data = obj.data
            else throw new Error('data是字符串或对象！')
            if(obj.method.toLowerCase() === 'get') obj.url += '?' + data
        }
        var xhr = new XMLHttpRequest;
        xhr.open(obj.method, obj.url, obj.async);
        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4){
                if(xhr.status >= 200 && xhr.status < 300){
                    let res
                    switch(obj.dataType) {
                        case 'xml':
                            res = xhr.responseXML
                        break
                        case 'json':
                            res = xhr.responseText
                            res = JSON.parse(res)
                        break
                        case 'text':
                            res = xhr.responseText
                        break
                        default:
                            throw new Error('dataType必须是json/text/xml！')
                    }
                    resolve(res)
                } else reject('错误')
            }
        }
        if(obj.method.toLowerCase() === 'post' && data) {
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
            xhr.send(data)
            return
        }
        xhr.send()
    })
}